add `--all` flag to `cargo-test`
authorAndy Russell <arussell123@gmail.com>
Thu, 20 Oct 2016 20:14:54 +0000 (16:14 -0400)
committerAndy Russell <arussell123@gmail.com>
Thu, 8 Dec 2016 21:11:19 +0000 (16:11 -0500)
16 files changed:
src/bin/bench.rs
src/bin/build.rs
src/bin/doc.rs
src/bin/install.rs
src/bin/run.rs
src/bin/rustc.rs
src/bin/rustdoc.rs
src/bin/test.rs
src/cargo/ops/cargo_compile.rs
src/cargo/ops/cargo_doc.rs
src/cargo/ops/cargo_package.rs
src/cargo/ops/cargo_rustc/context.rs
src/cargo/ops/cargo_rustc/job_queue.rs
src/cargo/ops/cargo_rustc/mod.rs
src/cargo/ops/mod.rs
tests/test.rs

index 774b3003cf7aaa77b7411036a9b5e89f2463ee04..bee11df314b67eb7d2a1804afe42b98d24044114 100644 (file)
@@ -88,7 +88,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult<Option<()>> {
             features: &options.flag_features,
             all_features: options.flag_all_features,
             no_default_features: options.flag_no_default_features,
-            spec: &options.flag_package,
+            spec: ops::Packages::Packages(&options.flag_package),
             release: true,
             mode: ops::CompileMode::Bench,
             filter: ops::CompileFilter::new(options.flag_lib,
index 7e6688b410fe9295211b9963689d9d13a0fa6c8e..446c87ea8e0548e5f4e209d61623c7d78add81df 100644 (file)
@@ -84,7 +84,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult<Option<()>> {
         features: &options.flag_features,
         all_features: options.flag_all_features,
         no_default_features: options.flag_no_default_features,
-        spec: &options.flag_package,
+        spec: ops::Packages::Packages(&options.flag_package),
         mode: ops::CompileMode::Build,
         release: options.flag_release,
         filter: ops::CompileFilter::new(options.flag_lib,
index f8b434247a9b17315395ce8bd2cddb949a4f968e..c28533a89de74dd380e7094dc9d31f6c67d537ec 100644 (file)
@@ -1,5 +1,5 @@
 use cargo::core::Workspace;
-use cargo::ops::{self, MessageFormat};
+use cargo::ops::{self, MessageFormat, Packages};
 use cargo::util::{CliResult, Config};
 use cargo::util::important_paths::{find_root_manifest_for_wd};
 
@@ -80,7 +80,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult<Option<()>> {
             features: &options.flag_features,
             all_features: options.flag_all_features,
             no_default_features: options.flag_no_default_features,
-            spec: &options.flag_package,
+            spec: Packages::Packages(&options.flag_package),
             filter: ops::CompileFilter::new(options.flag_lib,
                                             &options.flag_bin,
                                             &empty,
index c27db56ffaf9372237ccaa4d9a49a8adec5c66d8..e2ac62241b490c48f41b4f89d1c1d3691bec6c8e 100644 (file)
@@ -108,7 +108,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult<Option<()>> {
         features: &options.flag_features,
         all_features: options.flag_all_features,
         no_default_features: options.flag_no_default_features,
-        spec: &[],
+        spec: ops::Packages::Packages(&[]),
         mode: ops::CompileMode::Build,
         release: !options.flag_debug,
         filter: ops::CompileFilter::new(false, &options.flag_bin, &[],
index 1e7089a75722bbed7d9f66580f00de49b8ca8950..38fa657da9cc5a1a49fc147b526da48bd7b62bf5 100644 (file)
@@ -1,5 +1,5 @@
 use cargo::core::Workspace;
-use cargo::ops::{self, MessageFormat};
+use cargo::ops::{self, MessageFormat, Packages};
 use cargo::util::{CliResult, CliError, Config, Human};
 use cargo::util::important_paths::{find_root_manifest_for_wd};
 
@@ -81,7 +81,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult<Option<()>> {
         features: &options.flag_features,
         all_features: options.flag_all_features,
         no_default_features: options.flag_no_default_features,
-        spec: &[],
+        spec: Packages::Packages(&[]),
         release: options.flag_release,
         mode: ops::CompileMode::Build,
         filter: if examples.is_empty() && bins.is_empty() {
index a73088bfd64ad2b04362190d7879ad4cce80d33f..66369e2db938e6f67954cde58efd1b92aa0b302e 100644 (file)
@@ -1,7 +1,7 @@
 use std::env;
 
 use cargo::core::Workspace;
-use cargo::ops::{self, CompileOptions, CompileMode, MessageFormat};
+use cargo::ops::{self, CompileOptions, CompileMode, MessageFormat, Packages};
 use cargo::util::important_paths::{find_root_manifest_for_wd};
 use cargo::util::{CliResult, CliError, Config, human};
 
@@ -95,6 +95,8 @@ pub fn execute(options: Options, config: &Config) -> CliResult<Option<()>> {
         }
     };
 
+    let spec = options.flag_package.map_or_else(Vec::new, |s| vec![s]);
+
     let opts = CompileOptions {
         config: config,
         jobs: options.flag_jobs,
@@ -102,7 +104,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult<Option<()>> {
         features: &options.flag_features,
         all_features: options.flag_all_features,
         no_default_features: options.flag_no_default_features,
-        spec: &options.flag_package.map_or(Vec::new(), |s| vec![s]),
+        spec: Packages::Packages(&spec),
         mode: mode,
         release: options.flag_release,
         filter: ops::CompileFilter::new(options.flag_lib,
index 0d159f47a8e8cf9e1995f8b6e6c83538652d067d..41c29a4e8103e75a441a07a5aeff3a8f7a090bbb 100644 (file)
@@ -1,5 +1,5 @@
 use cargo::core::Workspace;
-use cargo::ops::{self, MessageFormat};
+use cargo::ops::{self, MessageFormat, Packages};
 use cargo::util::{CliResult, Config};
 use cargo::util::important_paths::{find_root_manifest_for_wd};
 
@@ -80,6 +80,8 @@ pub fn execute(options: Options, config: &Config) -> CliResult<Option<()>> {
     let root = find_root_manifest_for_wd(options.flag_manifest_path,
                                          config.cwd())?;
 
+    let spec = options.flag_package.map_or_else(Vec::new, |s| vec![s]);
+
     let doc_opts = ops::DocOptions {
         open_result: options.flag_open,
         compile_opts: ops::CompileOptions {
@@ -89,7 +91,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult<Option<()>> {
             features: &options.flag_features,
             all_features: options.flag_all_features,
             no_default_features: options.flag_no_default_features,
-            spec: &options.flag_package.map_or(Vec::new(), |s| vec![s]),
+            spec: Packages::Packages(&spec),
             release: options.flag_release,
             filter: ops::CompileFilter::new(options.flag_lib,
                                             &options.flag_bin,
index c28ff8fc431308c2707b147a190eef3ba984e69d..8884e773d0895a372419d74a484a332baadc49cb 100644 (file)
@@ -1,7 +1,7 @@
 use cargo::core::Workspace;
-use cargo::ops::{self, MessageFormat};
+use cargo::ops::{self, MessageFormat, Packages};
 use cargo::util::{CliResult, CliError, Human, human, Config};
-use cargo::util::important_paths::{find_root_manifest_for_wd};
+use cargo::util::important_paths::find_root_manifest_for_wd;
 
 #[derive(RustcDecodable)]
 pub struct Options {
@@ -28,6 +28,7 @@ pub struct Options {
     flag_no_fail_fast: bool,
     flag_frozen: bool,
     flag_locked: bool,
+    flag_all: bool,
 }
 
 pub const USAGE: &'static str = "
@@ -46,6 +47,7 @@ Options:
     --bench NAME                 Test only the specified benchmark target
     --no-run                     Compile, but don't run tests
     -p SPEC, --package SPEC ...  Package to run tests for
+    --all                        Test all packages in the workspace
     -j N, --jobs N               Number of parallel jobs, defaults to # of CPUs
     --release                    Build artifacts in release mode, with optimizations
     --features FEATURES          Space-separated list of features to also build
@@ -72,6 +74,9 @@ which indicates which package should be tested. If it is not given, then the
 current package is tested. For more information on SPEC and its format, see the
 `cargo help pkgid` command.
 
+All packages in the workspace are tested if the `--all` flag is supplied. The
+`--all` flag may be supplied in the presence of a virtual manifest.
+
 The --jobs argument affects the building of the test executable but does
 not affect how many jobs are used when running the tests.
 
@@ -111,6 +116,12 @@ pub fn execute(options: Options, config: &Config) -> CliResult<Option<()>> {
                                          &options.flag_bench);
     }
 
+    let spec = if options.flag_all {
+        Packages::All
+    } else {
+        Packages::Packages(&options.flag_package)
+    };
+
     let ops = ops::TestOptions {
         no_run: options.flag_no_run,
         no_fail_fast: options.flag_no_fail_fast,
@@ -122,7 +133,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult<Option<()>> {
             features: &options.flag_features,
             all_features: options.flag_all_features,
             no_default_features: options.flag_no_default_features,
-            spec: &options.flag_package,
+            spec: spec,
             release: options.flag_release,
             mode: mode,
             filter: filter,
@@ -139,7 +150,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult<Option<()>> {
         Some(err) => {
             Err(match err.exit.as_ref().and_then(|e| e.code()) {
                 Some(i) => CliError::new(human("test failed"), i),
-                None => CliError::new(Box::new(Human(err)), 101)
+                None => CliError::new(Box::new(Human(err)), 101),
             })
         }
     }
index e2f62e2593b9366004ad7801c63107a32426d6f4..e1bcc2ef7d9bf8dce9bad2a0899788914a7d4d38 100644 (file)
@@ -22,6 +22,7 @@
 //!       previously compiled dependency
 //!
 
+use std::borrow::Cow;
 use std::collections::HashMap;
 use std::path::PathBuf;
 
@@ -47,8 +48,8 @@ pub struct CompileOptions<'a> {
     pub all_features: bool,
     /// Flag if the default feature should be built for the root package
     pub no_default_features: bool,
-    /// Root package to build (if None it's the current one)
-    pub spec: &'a [String],
+    /// Root package to build (if empty it's the current one)
+    pub spec: Packages<'a>,
     /// Filter to apply to the root package to select which targets will be
     /// built.
     pub filter: CompileFilter<'a>,
@@ -79,6 +80,12 @@ pub enum MessageFormat {
     Json
 }
 
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub enum Packages<'a> {
+    All,
+    Packages(&'a [String]),
+}
+
 pub enum CompileFilter<'a> {
     Everything,
     Only {
@@ -92,8 +99,10 @@ pub enum CompileFilter<'a> {
 
 pub fn compile<'a>(ws: &Workspace<'a>, options: &CompileOptions<'a>)
                    -> CargoResult<ops::Compilation<'a>> {
-    for key in ws.current()?.manifest().warnings().iter() {
-        options.config.shell().warn(key)?
+    if let Some(root_package) = ws.current_opt() {
+        for key in root_package.manifest().warnings().iter() {
+            options.config.shell().warn(key)?
+        }
     }
     compile_ws(ws, None, options)
 }
@@ -112,8 +121,9 @@ pub fn resolve_dependencies<'a>(ws: &Workspace<'a>,
     let mut registry = PackageRegistry::new(ws.config())?;
 
     if let Some(source) = source {
-        registry.add_preloaded(ws.current()?.package_id().source_id(),
-                               source);
+        if let Some(root_package) = ws.current_opt() {
+            registry.add_preloaded(root_package.package_id().source_id(), source);
+        }
     }
 
     // First, resolve the root_package's *listed* dependencies, as well as
@@ -152,7 +162,6 @@ pub fn compile_ws<'a>(ws: &Workspace<'a>,
                       source: Option<Box<Source + 'a>>,
                       options: &CompileOptions<'a>)
                       -> CargoResult<ops::Compilation<'a>> {
-    let root_package = ws.current()?;
     let CompileOptions { config, jobs, target, spec, features,
                          all_features, no_default_features,
                          release, mode, message_format,
@@ -166,8 +175,19 @@ pub fn compile_ws<'a>(ws: &Workspace<'a>,
         bail!("jobs must be at least 1")
     }
 
+    let spec: Cow<'a, [String]> = match spec {
+        Packages::Packages(spec) => spec.into(),
+        Packages::All => ws.members()
+                           .map(|package| {
+                               let package_id = package.package_id();
+                               PackageIdSpec::from_package_id(package_id).to_string()
+                            })
+                           .collect()
+    };
+
     let profiles = ws.profiles();
     if spec.len() == 0 {
+        let root_package = ws.current()?;
         generate_targets(root_package, profiles, mode, filter, release)?;
     }
 
@@ -184,10 +204,11 @@ pub fn compile_ws<'a>(ws: &Workspace<'a>,
 
     let mut pkgids = Vec::new();
     if spec.len() > 0 {
-        for p in spec {
+        for p in spec.iter() {
             pkgids.push(resolve_with_overrides.query(&p)?);
         }
     } else {
+        let root_package = ws.current()?;
         pkgids.push(root_package.package_id());
     };
 
index 8c27335745e299d14a50d6c461c47a9878be543b..eb1894fde0a38f2a463c35e38084dca33dd417bb 100644 (file)
@@ -15,9 +15,18 @@ pub struct DocOptions<'a> {
 pub fn doc(ws: &Workspace, options: &DocOptions) -> CargoResult<()> {
     let package = ws.current()?;
 
+    let spec = match options.compile_opts.spec {
+        ops::Packages::Packages(packages) => packages,
+        _ => {
+            // This should not happen, because the `doc` binary is hard-coded to pass
+            // the `Packages::Packages` variant.
+            bail!("`cargo doc` does not support the `--all` flag")
+        },
+    };
+
     let mut lib_names = HashSet::new();
     let mut bin_names = HashSet::new();
-    if options.compile_opts.spec.is_empty() {
+    if spec.is_empty() {
         for target in package.targets().iter().filter(|t| t.documented()) {
             if target.is_lib() {
                 assert!(lib_names.insert(target.crate_name()));
@@ -37,10 +46,10 @@ pub fn doc(ws: &Workspace, options: &DocOptions) -> CargoResult<()> {
     ops::compile(ws, &options.compile_opts)?;
 
     if options.open_result {
-        let name = if options.compile_opts.spec.len() > 1 {
+        let name = if spec.len() > 1 {
             bail!("Passing multiple packages and `open` is not supported")
-        } else if options.compile_opts.spec.len() == 1 {
-            PackageIdSpec::parse(&options.compile_opts.spec[0])?
+        } else if spec.len() == 1 {
+            PackageIdSpec::parse(&spec[0])?
                 .name()
                 .replace("-", "_")
         } else {
index 26841796ec821a9a21f1c566cf4f2ddde5af2bae..c837e0156db7421e34b96b2ee3f7cfa7bd938112 100644 (file)
@@ -291,7 +291,7 @@ fn run_verify(ws: &Workspace, tar: &File, opts: &PackageOpts) -> CargoResult<()>
         features: &[],
         no_default_features: false,
         all_features: false,
-        spec: &[],
+        spec: ops::Packages::Packages(&[]),
         filter: ops::CompileFilter::Everything,
         release: false,
         message_format: ops::MessageFormat::Human,
index 9e0cc26eb6a56907fb2cb1d533d6c0a59d3cda00..de90ba89b260970b8bebadfd606cfd8ccab8e837 100644 (file)
@@ -31,7 +31,7 @@ pub struct Unit<'a> {
 pub struct Context<'a, 'cfg: 'a> {
     pub config: &'cfg Config,
     pub resolve: &'a Resolve,
-    pub current_package: PackageId,
+    pub current_package: Option<PackageId>,
     pub compilation: Compilation<'cfg>,
     pub packages: &'a PackageSet<'cfg>,
     pub build_state: Arc<BuildState>,
@@ -76,7 +76,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
             None => None,
         };
 
-        let current_package = ws.current()?.package_id().clone();
+        let current_package = ws.current_opt().map(Package::package_id).cloned();
         Ok(Context {
             host: host_layout,
             target: target_layout,
@@ -450,7 +450,8 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
         // we don't want to link it up.
         if src_dir.ends_with("deps") {
             // Don't lift up library dependencies
-            if unit.pkg.package_id() != &self.current_package && !unit.target.is_bin() {
+            if self.current_package.as_ref().map_or(false, |p| unit.pkg.package_id() != p)
+                && !unit.target.is_bin() {
                 None
             } else {
                 Some((
@@ -836,8 +837,9 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
     }
 
     pub fn show_warnings(&self, pkg: &PackageId) -> bool {
-        pkg == &self.current_package || pkg.source_id().is_path() ||
-            self.config.extra_verbose()
+        self.current_package.as_ref().map_or(false, |p| *pkg == *p)
+            || pkg.source_id().is_path()
+            || self.config.extra_verbose()
     }
 }
 
index e69bd33cd9157f32be5c93af68cfde29637697c8..6ddd002f84f0fcb1166830a5514850f50ecc6210 100644 (file)
@@ -198,7 +198,9 @@ impl<'a> JobQueue<'a> {
         }
 
         let build_type = if self.is_release { "release" } else { "debug" };
-        let profile = cx.lib_profile(&cx.current_package);
+        let profile = cx.current_package.as_ref().map_or_else(Profile::default, |p| {
+            cx.lib_profile(p).to_owned()
+        });
         let mut opt_type = String::from(if profile.opt_level == "0" { "unoptimized" }
                                         else { "optimized" });
         if profile.debuginfo {
index 186ce0d728cdef7d8aeda8e22176036511ff1100..0abfe49865505b09635e259f819d7b3ddc53ef3a 100644 (file)
@@ -557,7 +557,9 @@ fn build_base_args(cx: &mut Context,
     let prefer_dynamic = (unit.target.for_host() &&
                           !unit.target.is_custom_build()) ||
                          (crate_types.contains(&"dylib") &&
-                          unit.pkg.package_id() != &cx.current_package);
+                          cx.current_package.as_ref().map_or(false, |p| {
+                              *p != *unit.pkg.package_id()
+                          }));
     if prefer_dynamic {
         cmd.arg("-C").arg("prefer-dynamic");
     }
index 902c72e3007418886d1bfcbcc3a431e47eb459e4..2b46cc4d26a861acf31f52aafc0a4daaf4fbb3a8 100644 (file)
@@ -1,6 +1,6 @@
 pub use self::cargo_clean::{clean, CleanOptions};
 pub use self::cargo_compile::{compile, compile_ws, resolve_dependencies, CompileOptions};
-pub use self::cargo_compile::{CompileFilter, CompileMode, MessageFormat};
+pub use self::cargo_compile::{CompileFilter, CompileMode, MessageFormat, Packages};
 pub use self::cargo_read_manifest::{read_manifest,read_package,read_packages};
 pub use self::cargo_rustc::{compile_targets, Compilation, Kind, Unit};
 pub use self::cargo_rustc::Context;
index 6166a1ec651db8d5760fac2ac12dc64381c90ec9..1ed25b6f0b6796793352f8f8294ad002e213d77d 100644 (file)
@@ -9,6 +9,7 @@ use std::str;
 use cargotest::{sleep_ms, is_nightly};
 use cargotest::support::{project, execs, basic_bin_manifest, basic_lib_manifest};
 use cargotest::support::paths::CargoPathExt;
+use cargotest::support::registry::Package;
 use hamcrest::{assert_that, existing_file, is_not};
 use cargo::util::process;
 
@@ -2398,3 +2399,128 @@ fn test_many_with_features() {
                  .arg("--features").arg("foo"),
                 execs().with_status(0));
 }
+
+#[test]
+fn test_all_workspace() {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [project]
+            name = "foo"
+            version = "0.1.0"
+
+            [dependencies]
+            bar = { path = "bar" }
+
+            [workspace]
+        "#)
+        .file("src/main.rs", r#"
+            #[test]
+            fn foo_test() {}
+        "#)
+        .file("bar/Cargo.toml", r#"
+            [project]
+            name = "bar"
+            version = "0.1.0"
+        "#)
+        .file("bar/src/lib.rs", r#"
+            #[test]
+            fn bar_test() {}
+        "#);
+    p.build();
+
+    assert_that(p.cargo_process("test")
+                 .arg("--all"),
+                execs().with_stdout_contains("\
+running 1 test
+test foo_test ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+")
+                       .with_stdout_contains("\
+running 1 test
+test bar_test ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+"));
+}
+
+#[test]
+fn test_all_virtual_manifest() {
+    let p = project("workspace")
+        .file("Cargo.toml", r#"
+            [workspace]
+            members = ["a", "b"]
+        "#)
+        .file("a/Cargo.toml", r#"
+            [project]
+            name = "a"
+            version = "0.1.0"
+        "#)
+        .file("a/src/lib.rs", r#"
+            #[test]
+            fn a() {}
+        "#)
+        .file("b/Cargo.toml", r#"
+            [project]
+            name = "b"
+            version = "0.1.0"
+        "#)
+        .file("b/src/lib.rs", r#"
+            #[test]
+            fn b() {}
+        "#);
+    p.build();
+
+    assert_that(p.cargo_process("test")
+                 .arg("--all"),
+                execs().with_stdout_contains("\
+running 1 test
+test b ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+")
+                .with_stdout_contains("\
+running 1 test
+test b ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+"));
+}
+
+#[test]
+fn test_all_member_dependency_same_name() {
+    let p = project("workspace")
+        .file("Cargo.toml", r#"
+            [workspace]
+            members = ["a"]
+        "#)
+        .file("a/Cargo.toml", r#"
+            [project]
+            name = "a"
+            version = "0.1.0"
+
+            [dependencies]
+            a = "0.1.0"
+        "#)
+        .file("a/src/lib.rs", r#"
+            #[test]
+            fn a() {}
+        "#);
+    p.build();
+
+    Package::new("a", "0.1.0").publish();
+
+    assert_that(p.cargo_process("test")
+                 .arg("--all"),
+                execs().with_stdout_contains("\
+running 1 test
+test a ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+"));
+}